<template>
	<!-- #ifndef APP-NVUE -->
	<view class="yb-carousel yb-flex" :id="customIdSync" :class="customClassSync" :style="customStyleSync" ref="ybCarousel" :prop="carouselProp" :change:prop="carousel.propWatcher"
	@touchstart="carousel.touchstart" @touchmove="carousel.touchmove" @touchend="carousel.touchend">
	<!-- #endif -->
	<!-- #ifdef APP-NVUE -->
	<view class="yb-carousel yb-flex" :id="customIdSync" :class="customClassSync" :style="customStyleSync" ref="ybCarousel" @horizontalpan.stop="ontouchstart">
		<!-- #endif -->
		<yb-appear :disabled="appear" @appear="onAppear" custom-class="yb-flex-1">
			<template v-if="isShow">
				<view ref="ybCarouselFrame" class="yb-carousel-frame yb-flex yb-flex-1 yb-row" :style="frameStyle">
					<view class="yb-carousel-item yb-flex" ref="ybCarouselItem" :class="'yb-carousel-item_' + index"
					:style="{
						width: windowWidth + 'px',
						transform: 'translateX(' + (index * windowWidth) + 'px)',
						'margin-right': Util.pixelunit(nextMargin),
						'margin-left': Util.pixelunit(prevMargin),
						<!-- #ifdef APP-NVUE -->
						left: (3 * windowWidth) + 'px'
						<!-- #endif -->
					}" v-for="(item, index) in data" :key="index">
						<view class="yb-carousel-item-scale yb-flex yb-flex-1" :style="{
							'transform': 'scale(' + (scaleable && currentSync != index ? scaleSize : 1) + ')'
						}">
							<slot :name="index"></slot>
						</view>
					</view>
				</view>
				<slot name="dots">
					<view v-if="indicatorDotsSync.show" class="yb-carousel-dots yb-flex yb-justify-center" :class="{
						'yb-align-center': indicatorDotsSync.float == 'center',
						'yb-align-start': indicatorDotsSync.float == 'left',
						'yb-align-end': indicatorDotsSync.float == 'right'
					}">
						<view class="yb-flex yb-row yb-align-center yb-wrap">
							<view class="yb-carousel-dot"
							:class="{'yb-carousel-dot-focus': i == currentSync}"
							:style="{
								'background-color': i == currentSync ? indicatorDotsSync.focus : indicatorDotsSync.color,
								'border-color': indicatorDotsSync.border
							}"
							v-for="(dot, i) in data.length" :key="i"></view>
						</view>
					</view>
				</slot>
			</template>
		</yb-appear>
	</view>
</template>

<script>
	import Util from '../../js_sdk/util.js'
	import ComponentMixin from '../../js_sdk/componentMixin.js'
	// #ifdef APP-NVUE
	const Binding = uni.requireNativePlugin('bindingx')
	const animation = uni.requireNativePlugin('animation')
	// #endif
	let page
	let hides
	let shows
	export default {
		name: 'YbCarousel',
		mixins: [ComponentMixin],
		props: {
			//数据
			data: {
				type: Array,
				default () {
					return new Array
				}
			},
			//当前显示项索引
			current: {
				type: [Number, String],
				default: 0
			},
			//切换动画周期
			duration: {
				type: [Number, String],
				default: 300
			},
			//禁止触摸滑动
			disabledTouch: {
				type: Boolean,
				default: false
			},
			//禁用current变化引起的切换动画，注意该属性会影响到自动轮播的动画
			disableProgrammaticAnimation: {
				type: Boolean,
				default: false
			},
			prevMargin: {
				type: [Number, String],
				default: 0
			},
			nextMargin: {
				type: [Number, String],
				default: 0
			},
			//自动轮播
			autoplay: {
				type: Boolean,
				default: false
			},
			//自动轮播间隔
			interval: {
				type: [Number, String],
				default: 3000
			},
			//是否衔接滚动
			circular: {
				type: Boolean,
				default: false
			},
			//开启大小变化效果
			scaleable: {
				type: Boolean,
				default: false
			},
			scaleSize: {
				type: Number,
				default: 0.9
			},
			indicatorDots: {
				type: [Object, Boolean],
				default: false
			}
		},
		computed: {
			Util() {
				return Util
			},
			indicatorDotsSync () {
				return Object.assign({}, {
					show: false,
					color: '#333',
					border: '#eee',
					focus: '#eee',
					float: 'center'
				}, Util.typeof(this.indicatorDots) == 'Object' ? this.indicatorDots : Util.typeof(this.indicatorDots) == 'Boolean' ? { show: this.indicatorDots } : {})
			},
			frameStyle() {
				return this.filterStyle(
					// #ifdef APP-NVUE
					{
						width: ((this.data.length + 6) * this.windowWidth) + 'px',
						left: -(3 * this.windowWidth) + 'px'
					}
					// #endif
					// #ifndef APP-NVUE
					{
						transform: 'translateX(' + this.offset + 'px)',
					}
					// #endif
				) 
			},
			// #ifndef APP-NVUE
			carouselProp() {
				return {
					max: this.windowWidth * this.data.length,
					current: this.currentSync,
					windowWidth: this.windowWidth,
					length: this.data.length,
					circular: this.circular,
					scaleable: this.scaleable,
					duration: this.duration,
					disableProgrammaticAnimation: this.disableProgrammaticAnimation,
					autoplay: this.autoplay,
					interval: this.interval,
					disabledTouch: this.disabledTouch,
					isShow: this.isShow
				}
			}
			// #endif
		},
		data() {
			return {
				windowWidth: 0,
				isShow: false,
				offset: 0,
				currentSync: 0,
				// #ifdef APP-NVUE
				carousel_binding: null,
				carousel_refresh_binding: null,
				carousel_scroll_binding: null,
				isTouch: false,//正在触摸
				// #endif
				appear: false
			}
		},
		beforeDestroy() {
			// #ifdef APP-NVUE
			if ( this.carousel_binding ) {
				Binding.unbind({
					token: this.carousel_binding.token,
					eventType: 'pan'
				})
				this.carousel_binding = null
			}
			if ( this.carousel_refresh_binding ) {
				Binding.unbind({
					token: this.carousel_refresh_binding.token,
					eventType: 'timing'
				})
				this.carousel_refresh_binding = null
			}
			if ( this.carousel_scroll_binding ) {
				Binding.unbind({
					token: this.carousel_scroll_binding.token,
					eventType: 'timing'
				})
				this.carousel_scroll_binding = null
			}
			if ( this.autoplayTimer ) {
				clearTimeout(this.autoplayTimer)
				this.autoplayTimer = null
			}
			// #endif
		},
		methods: {
			onAppear() {
				if (!this.appear) {
					this.$nextTick(function() {
						setTimeout(() => {
							Util.getRect('.yb-carousel', this.$refs.ybCarousel, this).then(res => {
								this.windowWidth = res.width - Util.unitpixel(this.nextMargin) - Util.unitpixel(this.prevMargin)
								this.isShow = this.data.length > 0 ? true : false
								this.currentSync = parseInt(this.current)
								// #ifdef APP-NVUE
								if (  this.data.length > 1 ) {
									this.$nextTick(function () {
										 setTimeout(() => {
											 this.refresh().then(res => {
												if ( this.autoplay && this.data.length > 1 ) {
													this.autoplayTimer = setTimeout(() => {
														this.playto()
													}, this.interval)
												}
											 })
										 }, 50)
									})
								}
								// #endif
							})
						}, 50)
					})
					this.appear = true
				} else {
					this.isHide = false
				}
			},
			onChange(e) {
				this.offset = e.offset
				if (e.current >= 0 && e.current <= this.data.length - 1) {
					this.currentSync = e.current
					this.$emit('change', this.currentSync)
					this.$emit('update:current', this.currentSync)
					// #ifdef APP-NVUE
					if ( this.autoplay && this.data.length > 2 ) {
						if ( this.autoplayTimer ) {
							clearTimeout(this.autoplayTimer)
							this.autoplayTimer = null
						}
						this.autoplayTimer = setTimeout(() => {
							this.playto()
						}, this.interval)
					}
					// #endif
					
				}
			},
			// #ifdef APP-NVUE
			ontouchstart() {
				if ( !this.isTouch && this.data.length > 1 && !this.disabledTouch ) {
					if ( this.autoplayTimer ) {
						clearTimeout(this.autoplayTimer)
						this.autoplayTimer = null
					}
					this.isTouch = true
					this.refresh().then(e => {
						this.offset = -(this.currentSync * this.windowWidth)
						let props = [{
							element: Util.getEl(this.$refs.ybCarouselFrame),
							property: 'transform.translateX',
							expression: this.circular ?
								`abs(x)<${this.windowWidth * 0.75}?x+${this.offset}:(x>0?${this.offset + this.windowWidth * 0.75}:${this.offset - this.windowWidth * 0.75})` :
								`(${this.currentSync}==0&&x>0)||(${this.currentSync}==${this.data.length-1}&&x<0)?${this.offset}:(abs(x)<${this.windowWidth * 0.75}?x+${this.offset}:(x>0?${this.offset + this.windowWidth * 0.75}:${this.offset - this.windowWidth * 0.75}))`
						}]
						if (this.circular) {
							let prevCurrent = this.currentSync - 1 >= 0 ? this.currentSync - 1 : this.data.length - 1
							props.push({
								element: Util.getEl(this.$refs.ybCarouselItem[prevCurrent]),
								property: 'transform.translateX',
								expression: `x>0?${(this.currentSync - 1) * this.windowWidth}:${Binding.getComputedStyle(Util.getEl(this.$refs.ybCarouselItem[prevCurrent])).translateX}`
							})
							if (this.data.length > 3) {
								let prevprevCurrent = prevCurrent - 1 >= 0 ? prevCurrent - 1 : this.data.length - 1
								props.push({
									element: Util.getEl(this.$refs.ybCarouselItem[prevprevCurrent]),
									property: 'transform.translateX',
									expression: `x>0?${(this.currentSync - 2) * this.windowWidth}:${Binding.getComputedStyle(Util.getEl(this.$refs.ybCarouselItem[prevprevCurrent])).translateX}`
								})
							}
							let nextCurrent = this.currentSync + 1 <= this.data.length - 1 ? this.currentSync + 1 : 0
							props.push({
								element: Util.getEl(this.$refs.ybCarouselItem[nextCurrent]),
								property: 'transform.translateX',
								expression: `x<0?${(this.currentSync + 1) * this.windowWidth}:${Binding.getComputedStyle(Util.getEl(this.$refs.ybCarouselItem[nextCurrent])).translateX}`
							})
							if (this.data.length > 3) {
								let nextnextCurrent = nextCurrent + 1 <= this.data.length - 1 ? nextCurrent + 1 : 0
								props.push({
									element: Util.getEl(this.$refs.ybCarouselItem[nextnextCurrent]),
									property: 'transform.translateX',
									expression: `x<0?${(this.currentSync + 2) * this.windowWidth}:${Binding.getComputedStyle(Util.getEl(this.$refs.ybCarouselItem[nextnextCurrent])).translateX}`
								})
							}
						}
						this.carousel_binding = Binding.bind({
							anchor: Util.getEl(this.$refs.ybCarousel),
							eventType: 'pan',
							props: props
						}, (e) => {
							if ((e.state == 'end' || e.state == 'cancel') && this.carousel_binding) {
								Binding.unbind({
									token: this.carousel_binding.token,
									eventType: 'pan'
								})
								this.carousel_binding = null
								if (!this.circular && ((this.currentSync == 0 && e.deltaX > 0) || (this.currentSync == this.data.length - 1 && e.deltaX < 0))) {
									if ( this.autoplay && this.data.length > 2 ) {
										this.autoplayTimer = setTimeout(() => {
											this.playto()
										}, this.interval)
									}
									this.$nextTick(function () {
										this.isTouch = false
									})
									return
								}
								if (Math.abs(e.deltaX) > this.windowWidth / 5) {
									let offset = e.deltaX > 0 ? this.offset + this.windowWidth : this.offset - this
										.windowWidth
									let current = e.deltaX > 0 ? (this.currentSync - 1 >= 0 ? this.currentSync - 1 : this.data
										.length - 1) : (this.currentSync + 1 <= this.data.length - 1 ? this.currentSync + 1 :
										0)
									let nextCurrent = e.deltaX > 0 ? (current - 1 >= 0 ? current - 1 : this.data
										.length - 1) : (current + 1 <= this.data.length - 1 ? current + 1 :
										0)
									if ( this.data.length > 2 && this.circular ) {
										animation.transition(this.$refs.ybCarouselItem[nextCurrent], {
											styles: {
												transform: `translateX(${ (e.deltaX > 0 ? this.currentSync - 2 : this.currentSync + 2) * this.windowWidth}px)`
											},
											duration: 0,
											needLayout: true
										}, (res) => {
											animation.transition(this.$refs.ybCarouselFrame, {
												styles: {
													transform: `translateX(${offset}px)`
												},
												duration: this.duration,
												needLayout: true
											}, (res) => {
												this.isTouch = false
											})
											this.onChange({
												current: current,
												offset: offset
											})
										})
									} else {
										
										animation.transition(this.$refs.ybCarouselFrame, {
											styles: {
												transform: `translateX(${offset}px)`
											},
											duration: this.duration,
											needLayout: true
										}, (res) => {
											this.isTouch = false
										})
										this.onChange({
											current: current,
											offset: offset
										})
									}
								} else {
									animation.transition(this.$refs.ybCarouselFrame, {
										styles: {
											transform: `translateX(${this.offset}px)`
										},
										duration: this.duration,
										needLayout: true
									}, (res) => {
										this.isTouch = false
									})
								}
							}
						})
					})
				}
			},
			refresh () {
				return new Promise(resolve => {
					let props = [{
						element: Util.getEl(this.$refs.ybCarouselFrame),
						property: 'transform.translateX',
						expression: `linear(t, ${this.offset}, ${-(this.currentSync * this.windowWidth) - this.offset}, 1)`
					}]
					if ( this.circular ) {
						for (let i = 0; i < this.data.length; i++) {
							let prevCurrent = this.currentSync - 1 >= 0 ? this.currentSync - 1 : this.data.length - 1
							let nextCurrent = this.currentSync + 1 <= this.data.length - 1 ? this.currentSync + 1 : 0
							let offset = 0
							if (i == prevCurrent) {
								offset = (this.currentSync - 1) * this.windowWidth
							} else if (i == nextCurrent) {
								offset = (this.currentSync + 1) * this.windowWidth
							} else {
								offset = i * this.windowWidth
							}
							props.push({
								element: Util.getEl(this.$refs.ybCarouselItem[i]),
								property: 'transform.translateX',
								expression: `linear(t, ${Binding.getComputedStyle(Util.getEl(this.$refs.ybCarouselItem[i])).translateX}, ${offset - Binding.getComputedStyle(Util.getEl(this.$refs.ybCarouselItem[i])).translateX}, 1)`
							})
						}
					}
					this.carousel_refresh_binding = Binding.bind({
						eventType: 'timing',
						exitExpression: 't>1',
						props: props
					}, (e) => {
						if (e.state == 'exit' && this.carousel_refresh_binding && e.t > 1) {
							Binding.unbind({
								token: this.carousel_refresh_binding.token,
								eventType: 'timing'
							})
							this.carousel_refresh_binding = null
							resolve(e)
						}
					})
				})
			},
			playto () {
				//清除自动轮播定时器
				if (this.autoplayTimer) {
					clearTimeout(this.autoplayTimer)
					this.autoplayTimer = null
				}
				this.refresh().then(res => {
					this.offset = -(this.currentSync * this.windowWidth)
					let current = this.currentSync + 1 <= this.data.length - 1 ? this.currentSync + 1 : 0
					let nextCurrent = current + 1 <= this.data.length - 1 ? current + 1 : 0
					if ( this.data.length > 2 && this.circular ) {
						let offset = this.offset - this.windowWidth
						animation.transition(this.$refs.ybCarouselItem[nextCurrent], {
							styles: {
								transform: `translateX(${(this.currentSync + 2) * this.windowWidth}px)`
							},
							duration: 0,
							needLayout: true
						}, (res) => {
							animation.transition(this.$refs.ybCarouselFrame, {
								styles: {
									transform: `translateX(${offset}px)`
								},
								duration: this.duration,
								needLayout: true
							}, (res) => {
							})
							this.onChange({
								current: current,
								offset: offset
							})
						})
					} else {
						let offset = -(current * this.windowWidth)
						animation.transition(this.$refs.ybCarouselFrame, {
							styles: {
								transform: `translateX(${offset}px)`
							},
							duration: this.duration,
							needLayout: true
						}, (res) => {
						})
						this.onChange({
							current: current,
							offset: offset
						})
					}
				})
			},
			scrollTo (current) {
				if (current > -1 && current < this.data.length && !this.isTouch) {
					if (this.autoplayTimer) {
						clearTimeout(this.autoplayTimer)
						this.autoplayTimer = null
					}
					this.refresh().then(e => {
						let offset = -(current * this.windowWidth)
						if ( this.data.length > 2 && this.circular ) {
							let prevCurrent = current - 1 >= 0 ? current - 1 : this.data.length - 1
							let nextCurrent = current + 1 <= this.data.length - 1 ? current + 1 : 0
							let duration = this.disableProgrammaticAnimation ? 1 : this.duration
							this.carousel_scroll_binding = Binding.bind({
								eventType: 'timing',
								exitExpression: 't>' + duration,
								props: [{
									element: Util.getEl(this.$refs.ybCarouselItem[current]),
									property: 'transform.translateX',
									expression: `linear(t, ${this.currentSync * this.windowWidth}, ${current * this.windowWidth - this.currentSync * this.windowWidth}, 1)`
								},{
									element: Util.getEl(this.$refs.ybCarouselItem[prevCurrent]),
									property: 'transform.translateX',
									expression: `linear(t, ${(this.currentSync - 1) * this.windowWidth}, ${(current - 1) * this.windowWidth - (this.currentSync - 1) * this.windowWidth}, 1)`
								},{
									element: Util.getEl(this.$refs.ybCarouselItem[nextCurrent]),
									property: 'transform.translateX',
									expression: `linear(t, ${(this.currentSync + 1) * this.windowWidth}, ${(current + 1) * this.windowWidth - (this.currentSync + 1) * this.windowWidth}, 1)`
								},{
									element: Util.getEl(this.$refs.ybCarouselFrame),
									property: 'transform.translateX',
									expression: `linear(t, ${-(this.currentSync * this.windowWidth)}, ${offset + (this.currentSync * this.windowWidth)}, ${duration})`
								}]
							}, (e) => {
								if (e.state == 'exit' && this.carousel_scroll_binding && e.t > duration) {
									Binding.unbind({
										token: this.carousel_scroll_binding.token,
										eventType: 'timing'
									})
									this.carousel_scroll_binding = null
									this.onChange({
										current: current,
										offset: offset
									})
								}
							})
						} else {
							animation.transition(this.$refs.ybCarouselFrame, {
								styles: {
									transform: `translateX(${offset}px)`
								},
								duration:  this.disableProgrammaticAnimation ? 0 : this.duration,
								needLayout: true
							}, (res) => {
								this.onChange({
									current: current,
									offset: offset
								})
							})
						}
					})
				}
			}
			// #endif
		},
		watch: {
			current(newVal) {
				if (this.currentSync != newVal) {
					// #ifndef APP-NVUE
					this.currentSync = parseInt(newVal)
					// #endif
					// #ifdef APP-NVUE
					this.scrollTo(newVal)
					// #endif
				}
			},
			data(newVal) {
				this.isShow = newVal.length > 0
				// #ifdef APP-NVUE
				this.$nextTick(function () {
					if ( this.autoplay ) {
						this.autoplayTimer = setTimeout(() => {
							this.playto()
						}, this.interval + 50)
					} else {
						setTimeout(() => {
							this.refresh()
						}, 50)
					}
				})
				// #endif
			}
		}
	}
</script>

<!-- #ifdef APP-VUE || H5 || MP-WEIXIN || MP-QQ -->
<script module="carousel" lang="wxs">
	function initState(ins) {
		var state = ins.getState()
		state.isTouch = false //是否正在触摸
		state.startX = 0 //初始触摸位置X轴
		state.startY = 0 //初始触摸Y轴位置
		state.threshold = 0 //阙值
		state.offset = 0 //总偏移量
		state.windowWidth = 0 //可视宽度
		state.length = 0 //轮播项总数量
		state.current = 0 //当前显示项索引
		state.circular = false //是否衔接滚动
		state.duration = 300 //滑动动画周期
		state.disabledTouch = false //禁止触摸滑动
		state.interval = 3000 //自动轮播间隔
		state.autoplay = false //允许自动轮播
		state.disableProgrammaticAnimation = false //禁用current变化引起切换的动画
		state.autoplayTimer = null //自动轮播控制器
	}

	function touchstart(event, ins) {
		var state = ins.getState()
		if (state.autoplayTimer) {
			ins.clearTimeout(state.autoplayTimer)
			state.autoplayTimer = null
		}
		if (state.length < 2 || state.disabledTouch) {
			return
		}
		state.isTouch = true
		state.threshold = 0
		var touch = event.touches[0] || event.changedTouches[0]
		state.startX = touch.pageX
		state.startY = touch.pageY
	}

	function touchmove(event, ins) {
		event.stopPropagation && event.stopPropagation()
		var state = ins.getState()
		if (state.isTouch) {
			var touch = event.touches[0] || event.changedTouches[0]
			var pageX = touch.pageX
			if ((Math.abs(touch.pageY - state.startY) < Math.abs(touch.pageX - state.startX)) && Math.abs(touch.pageY -
					state.startY) < 20) {
				state.threshold = touch.pageX - state.startX
				if (state.offset + state.threshold > state.offset) {
					//左滑
					if (state.circular) {
						var prevCurrent = state.current - 1 >= 0 ? state.current - 1 : state.length - 1
						ins.selectComponent('.yb-carousel-item_' + prevCurrent).setStyle({
							transform: 'translateX(' + ((state.current - 1) * state.windowWidth) + 'px)',
							transition: ''
						})
						if (state.length > 2) {
							var prevprevCurrent = prevCurrent - 1 >= 0 ? prevCurrent - 1 : state.length - 1
							ins.selectComponent('.yb-carousel-item_' + prevprevCurrent).setStyle({
								transform: 'translateX(' + ((state.current - 2) * state.windowWidth) + 'px)',
								transition: ''
							})
						}
					} else {
						if (state.current == 0) {
							state.threshold = 0
						}
					}
				} else {
					//右滑
					if (state.circular) {
						var nextCurrent = state.current + 1 <= state.length - 1 ? state.current + 1 : 0
						ins.selectComponent('.yb-carousel-item_' + nextCurrent).setStyle({
							transform: 'translateX(' + ((state.current + 1) * state.windowWidth) + 'px)',
							transition: ''
						})
						if (state.length > 2) {
							var nextnextCurrent = nextCurrent + 1 <= state.length - 1 ? nextCurrent + 1 : 0
							ins.selectComponent('.yb-carousel-item_' + nextnextCurrent).setStyle({
								transform: 'translateX(' + ((state.current + 2) * state.windowWidth) + 'px)',
								transition: ''
							})
						}
					} else {
						if (state.current == state.length - 1) {
							state.threshold = 0
						}
					}
				}
				if (state.threshold < -state.windowWidth) {
					state.threshold = -state.windowWidth
				}
				if (state.threshold > state.windowWidth) {
					state.threshold = state.windowWidth
				}
				ins.selectComponent('.yb-carousel-frame').setStyle({
					transform: 'translateX(' + (state.offset + state.threshold) + 'px)',
					transition: ''
				})
				return false
			}
		}
	}

	function touchend(event, ins) {
		var state = ins.getState()
		if (state.isTouch) {
			if (state.offset + state.threshold > state.offset) {
				//左滑
				if (Math.abs(state.threshold) > state.windowWidth / 5) {
					state.offset = state.offset + state.windowWidth
					state.current = state.current - 1 >= 0 ? state.current - 1 : state.length - 1
				}
			} else {
				//右滑
				if (Math.abs(state.threshold) > state.windowWidth / 5) {
					state.offset = state.offset - state.windowWidth
					state.current = state.current + 1 <= state.length - 1 ? state.current + 1 : 0
				}
			}
			ins.selectComponent('.yb-carousel-frame').setStyle({
				transform: 'translateX(' + state.offset + 'px)',
				transition: 'transform ' + state.duration + 'ms'
			})
			ins.callMethod('onChange', {
				current: state.current,
				offset: state.offset
			})
			if (state.circular) {
				ins.setTimeout(function() {
					refresh(ins)
					state.isTouch = false
					if (state.autoplay) {
						state.autoplayTimer = ins.setTimeout(function() {
							autoplay(ins)
							ins.clearTimeout(state.autoplayTimer)
							state.autoplayTimer = null
						}, state.duration + state.interval)
					}
				}, state.duration)
			} else {
				ins.callMethod('onChange', {
					current: state.current,
					offset: state.offset
				})
				state.isTouch = false
				if (state.autoplay) {
					state.autoplayTimer = ins.setTimeout(function() {
						autoplay(ins)
						ins.clearTimeout(state.autoplayTimer)
						state.autoplayTimer = null
					}, state.duration + state.interval)
				}
			}
		}
	}

	function refresh(ins) {
		var state = ins.getState()
		ins.selectComponent('.yb-carousel-frame').setStyle({
			transform: 'translateX(' + (-state.current * state.windowWidth) + 'px)',
			transition: ''
		})
		state.offset = -(state.current * state.windowWidth)
		for (var i = 0; i < state.length; i++) {
			var prevCurrent = state.current - 1 >= 0 ? state.current - 1 : state.length - 1
			var nextCurrent = state.current + 1 <= state.length - 1 ? state.current + 1 : 0
			var offset = 0
			if (i == prevCurrent) {
				offset = (state.current - 1) * state.windowWidth
			} else if (i == nextCurrent) {
				offset = (state.current + 1) * state.windowWidth
			} else {
				offset = i * state.windowWidth
			}
			ins.selectComponent('.yb-carousel-item_' + i).setStyle({
				transform: 'translateX(' + offset + 'px)',
				transition: ''
			})
		}
		ins.callMethod('onChange', {
			current: state.current,
			offset: state.offset
		})
	}

	function autoplay(ins) {
		var state = ins.getState()
		//清除自动轮播定时器
		if (state.autoplayTimer) {
			ins.clearTimeout(state.autoplayTimer)
			state.autoplayTimer = null
		}
		var nextCurrent = state.current + 1 <= state.length - 1 ? state.current + 1 : 0
		if (state.circular) {
			if (state.length > 2 && ins.selectComponent('.yb-carousel-item_' + nextCurrent)) {
				ins.selectComponent('.yb-carousel-item_' + nextCurrent).setStyle({
					transform: 'translateX(' + ((state.current + 1) * state.windowWidth) + 'px)',
					transition: ''
				})
				var nextnextCurrent = nextCurrent + 1 <= state.length - 1 ? nextCurrent + 1 : 0
				ins.selectComponent('.yb-carousel-item_' + nextnextCurrent).setStyle({
					transform: 'translateX(' + ((state.current + 2) * state.windowWidth) + 'px)',
					transition: ''
				})
			}
		}
		state.current = nextCurrent
		state.offset = state.circular ? state.offset - state.windowWidth : -(state.current * state.windowWidth)
		ins.selectComponent('.yb-carousel-frame').setStyle({
			transform: 'translateX(' + state.offset + 'px)',
			transition: 'transform ' + state.duration + 'ms'
		})
		ins.callMethod('onChange', {
			current: state.current,
			offset: state.offset
		})
		if (state.circular) {
			ins.setTimeout(function() {
				refresh(ins)
				state.isTouch = false
				if (state.autoplay && !state.isHide) {
					state.autoplayTimer = ins.setTimeout(function() {
						autoplay(ins)
					}, state.duration + state.interval)
				}
			}, state.duration)
		} else {
			ins.callMethod('onChange', {
				current: state.current,
				offset: state.offset
			})
			state.isTouch = false
			if (state.autoplay && !state.isHide) {
				state.autoplayTimer = ins.setTimeout(function() {
					autoplay(ins)
				}, state.duration + state.interval)
			}
		}
	}

	function scrollTo(current, ins) {
		var state = ins.getState()
		if (state.current != current) {
			if (current > -1 && current < state.length && !state.isTouch) {
				if (state.autoplayTimer) {
					ins.clearTimeout(state.autoplayTimer)
					state.autoplayTimer = null
				}
				state.current = current
				state.offset = -(state.current * state.windowWidth)
				if (state.circular && state.length > 1) {
					refresh(ins)
				}
				ins.callMethod('onChange', {
					current: state.current,
					offset: state.offset
				})
				ins.selectComponent('.yb-carousel-frame').setStyle({
					transform: 'translateX(' + state.offset + 'px)',
					transition: state.disableProgrammaticAnimation ? '' : 'transform ' + state.duration + 'ms'
				})
				if (state.autoplay && state.length > 1 && !state.isTouch) {
					state.autoplayTimer = ins.setTimeout(function() {
						autoplay(ins)
					}, state.duration + state.interval)
				}
			}
		}
	}

	function propWatcher(newVal, oldVal, ins) {
		if ( oldVal ) {
			var state = ins.getState()
			if (state && !state.length) {
				initState(ins)
			}
			if ( state ) {
				state.windowWidth = newVal.windowWidth
				state.length = newVal.length
				state.autoplay = newVal.autoplay
				state.disabledTouch = newVal.disabledTouch
				state.scaleable = newVal.scaleable
				state.circular = newVal.circular
				state.duration = newVal.duration
				state.interval = newVal.interval
				state.disableProgrammaticAnimation = newVal.disableProgrammaticAnimation
				if (newVal.current != oldVal.current) {
					if (newVal.isShow) {
						scrollTo(newVal.current, ins)
					}
				}
				if (newVal.isShow != oldVal.isShow) {
					if (newVal.isShow) {
						ins.setTimeout(function() {
							if (state.circular && state.length > 1) {
								refresh(ins)
							}
							if (state.current != newVal.current) {
								scrollTo(newVal.current, ins)
							} else {
								if (newVal.autoplay && state.length > 1 && !state.isTouch) {
									if (state.autoplayTimer) {
										ins.clearTimeout(state.autoplayTimer)
										state.autoplayTimer = null
									}
									state.autoplayTimer = ins.setTimeout(function() {
										autoplay(ins)
									}, state.interval)
								}
							}
						}, 100)
					}
				}
				if (newVal.isHide != oldVal.isHide) {
					if (newVal.isHide) {
						if (state.autoplayTimer) {
							ins.clearTimeout(state.autoplayTimer)
							state.autoplayTimer = null
						}
					} else {
						if (state.autoplay && newVal.isShow) {
							state.autoplayTimer = ins.setTimeout(function() {
								autoplay(ins)
							}, state.interval)
						}
					}
				}
			}
		}
	}
	module.exports = {
		touchstart: touchstart,
		touchmove: touchmove,
		touchend: touchend,
		refresh: refresh,
		propWatcher: propWatcher
	}
</script>
<!-- #endif -->

<style scoped>
	.yb-carousel {
		position: relative;
	}
	.yb-carousel-dots {
		position: absolute;
		bottom: 30rpx;
		left: 30rpx;
		right: 30rpx;
	}
	.yb-carousel-dot {
		width: 10rpx;
		height: 10rpx;
		border-radius: 10rpx;
		background-color: #333;
		margin: 0 5rpx;
		border-width: 1px;
		border-style: solid;
		transition-property: width,backgroundColor;
		transition-duration: .1s;
	}
	.yb-carousel-dot-focus {
		width: 20rpx;
	}
	.yb-carousel-wrapper {
		position: absolute;
		top: 0;
		bottom: 0;
	}

	.yb-carousel-frame {
		position: absolute;
		top: 0;
		left: 0;
		bottom: 0;
		/* #ifndef APP-NVUE */
		right: 0;
		overflow: visible;
		/* #endif */
	}

	.yb-carousel-item {
		position: absolute;
		top: 0;
		/* #ifndef APP-NVUE */
		left: 0;
		right: 0;
		/* #endif */
		bottom: 0;
	}
	.yb-carousel-item-scale {
		transition: transform .3s;
	}
</style>
